
#pragma once

#include<iostream>
#include <unistd.h>
#include<stdlib.h>
#include<cstring>
#include <sys/types.h>          
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <strings.h>
#include<unordered_map>
#include<functional>

using namespace std;

#include"Log.hpp"

typedef function<string(const string&)> func_t; 

uint16_t defaultport=8080;
string defaultip="0.0.0.0";
const int sizes=1024;

Log lg;

// string func(string &info)
// {
//     string buffer="Server receive:";
//     buffer+=info;
//     return buffer;
// }

enum{
    SOCKET_ERR=1,
    BIND_ERR
};


class UpdServer
{
public:
    UpdServer(const uint16_t&port=defaultport,const string &ip=defaultip)
        :sockfd_(0),ip_(ip),port_(port),isrunning_(false)
    {}
    void Init()
    {
        //socket->bind->recvfrom->sendto
        //int socket(int domain, int type, int protocol);//domain什么协议(ipv4)，type什么方式(udp)
        sockfd_=socket(AF_INET,SOCK_DGRAM,0);//创建udo socket
        if(sockfd_<0){
            lg(Fatal,"socket create failed,socket:%d",socket);
            exit(SOCKET_ERR);
        }
        lg(Info, "socket create success, sockfd: %d", sockfd_);
        struct sockaddr_in local;
        bzero(&(local),sizeof(local));
        local.sin_family=AF_INET;//
        local.sin_port=htons(port_);//要转成网络序列（即把不是大端的转化成大端）
        local.sin_addr.s_addr=inet_addr(ip_.c_str());//把我们传入的"13.3.3.3"这种地址转化成数字网络序列；
        socklen_t len=sizeof(local);
        int n=bind(sockfd_,(sockaddr *)&local,len);
        if(n<0){
            lg(Fatal,"bind error,error:%d,err string:%s",errno,strerror(errno));
            exit(BIND_ERR);
        }
        lg(Info, "bind success, errno: %d, err string: %s", errno, strerror(errno));
    }
    void Run()
    {
        isrunning_=true;
        char inbuffer[sizes];
        while(isrunning_)
        {
            struct sockaddr_in client;
            socklen_t len=sizeof(client);
            int n=recvfrom(sockfd_,inbuffer,sizeof(inbuffer)-1,0,(sockaddr*)&client,&len);
            if(n<0)
            {
                lg(Warning, "recvfrom error, errno: %d, err string: %s", errno, strerror(errno));
                continue;
            }
            inbuffer[n]=0;
            string info=inbuffer;
           // string echo_string=func(info);
            string clientip=inet_ntoa(client.sin_addr);
            uint16_t clientport=client.sin_port;
            CheckUser(client,clientip,clientport);
            Broadcast(info,clientip,clientport);
            //sendto(sockfd_, echo_string.c_str(), echo_string.size(),0,(sockaddr*)&client,len);
        }

    }
    void CheckUser(const struct sockaddr_in &client, const std::string clientip, uint16_t clientport)
    {
        auto iter=online_user_.find(clientip);
        if(iter==online_user_.end())
        {
            online_user_.insert({clientip,client});
            cout<<"["<<clientip<<":"<<clientport<<"]:"<<"add a user"<<endl;
        }
        if(online_user_[clientip].sin_port!=clientport){
            online_user_[clientip].sin_port=clientport;
        }
    }
    void Broadcast(string &info,string ip,uint16_t port)
    {
        for(const auto& user:online_user_){
            string message="[";
            message+=ip;
            message+=":";
            message+=to_string(port);
            message+="]";
            message+=info;
            socklen_t len=sizeof(user.second);
            sendto(sockfd_, message.c_str(), message.size(),0,(sockaddr*)(&user.second),len);
            cout<<message<<endl;
        }
    }

    ~UpdServer()
    {
        if(sockfd_>0) close(sockfd_);
    }
private:
    int sockfd_;//网络文件表述符
    string ip_;//公网ip
    uint16_t port_;//端口
    unordered_map<string,sockaddr_in>online_user_;
    bool isrunning_;
};